import tkinter as tk
from tkinter import ttk, messagebox
from PIL import Image, ImageTk
import os
import threading
import socket
#import cv2
import random
from tkinter import filedialog
import time
import requests
from datetime import datetime
import imaplib
import email
from email.header import decode_header
import pygame
import pygame.mixer
import numpy as np

# ===== 用户配置区域 =====
BACKGROUND_IMAGE = "background.jpg"
OPENWEATHER_API_KEY = '8d5f40d67536db2d8cadd74dc2a1a99f'
EMAIL_CONFIG = {
    "account": "3268447338@qq.com",
    "password": "nfiimndwjfvvcigd",
    "server": "imap.qq.com"
}
CHENGDU_LOCATION = {
    "lat": 30.659462,
    "lng": 104.065735
}
SERVER_IP = '192.168.184.100'
# ======================

class AppManager:
    def __init__(self):
        self.root = tk.Tk()
        self.root.withdraw()
        self.server_ip = SERVER_IP
        self.server_port = 12581
        self.show_login()
        self.root.mainloop()

    def show_login(self):
        LoginWindow(self.root, self)
    
    def show_main(self):
        MainWindow(self.root, self)

class LoginWindow:
    """现代化登录窗口（带背景图片）"""
    def __init__(self, root, app_manager):
        self.app = app_manager
        self.window = tk.Toplevel(root)
        self._setup_ui()
    
    def _create_round_rect(self, x1, y1, x2, y2, radius=25, **kwargs):
        """创建圆角矩形"""
        points = [
            x1+radius, y1,
            x2-radius, y1,
            x2, y1,
            x2, y1+radius,
            x2, y2-radius,
            x2, y2,
            x2-radius, y2,
            x1+radius, y2,
            x1, y2,
            x1, y2-radius,
            x1, y1+radius,
            x1, y1
        ]
        return self.login_card.create_polygon(points, smooth=True, splinesteps=32, **kwargs)

    def _setup_ui(self):
        """初始化界面"""
        self.window.title("智能家居系统 - 安全登录")
        self.window.attributes('-fullscreen', True)
        
        # 创建主容器
        self.main_container = tk.Canvas(self.window, highlightthickness=0)
        self.main_container.pack(fill=tk.BOTH, expand=True)
        
        # 加载背景图片
        self._load_background()
        
        # 创建登录卡片
        self._create_login_card()
        self._create_widgets()
        self._bind_events()

    def _load_background(self):
        """加载背景图片"""
        try:
            script_dir = os.path.dirname(os.path.realpath(__file__))
            img_path = os.path.join(script_dir, BACKGROUND_IMAGE)
            
            img = Image.open(img_path)
            screen_width = self.window.winfo_screenwidth()
            screen_height = self.window.winfo_screenheight()
            
            # 保持宽高比缩放
            img_ratio = img.width / img.height
            screen_ratio = screen_width / screen_height
            
            if img_ratio > screen_ratio:
                new_width = screen_width
                new_height = int(new_width / img_ratio)
            else:
                new_height = screen_height
                new_width = int(new_height * img_ratio)
                
            resized_img = img.resize((new_width, new_height), Image.LANCZOS)
            self.bg_image = ImageTk.PhotoImage(resized_img)
            
            # 创建背景
            bg_label = tk.Label(self.main_container, image=self.bg_image)
            bg_label.place(x=0, y=0, relwidth=1, relheight=1)
            
            # 添加遮罩层
            self.main_container.create_rectangle(0, 0, screen_width, screen_height, 
                                               fill="#000000", stipple="gray50", alpha=0.2)
            
        except Exception as e:
            self.main_container.configure(bg='#f0f0f0')  # 使用浅灰色作为备用

    def _create_login_card(self):
        """创建登录卡片"""
        self.login_card = tk.Canvas(self.main_container, 
                                  width=400, 
                                  height=500,
                                  bg='#ffffff', 
                                  highlightthickness=0)
        self.login_card.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
        self._create_round_rect(0, 0, 400, 500, radius=20, fill="#ffffff")

    def _create_widgets(self):
        """创建界面组件"""
        # 标题
        self.login_card.create_text(200, 80, text="智能家居系统",
                                  font=('微软雅黑', 24, 'bold'), fill='#2c3e50')
        
        # 输入框
        self._create_input_field("用户名", 150)
        self._create_input_field("密码", 250, is_password=True)
        
        # 登录按钮
        self.login_btn = tk.Button(self.login_card, 
                                 text="登  录", 
                                 command=self._attempt_login,
                                 font=('微软雅黑', 14),
                                 bg='#3498db', 
                                 fg='white',
                                 relief=tk.FLAT)
        self.login_btn.place(x=100, y=350, width=200, height=45)

    def _create_input_field(self, label_text, y_pos, is_password=False):
        """创建输入框"""
        entry = ttk.Entry(self.login_card, 
                         show="*" if is_password else None,
                         font=('微软雅黑', 12))
        entry.place(x=50, y=y_pos+30, width=300, height=45)
        
        label = self.login_card.create_text(55, y_pos+15, 
                                          text=label_text,
                                          font=('微软雅黑', 11), 
                                          fill='#95a5a6',
                                          anchor=tk.W)
        
        # 绑定动画事件
        entry.bind('<FocusIn>', lambda e: self._float_label_up(label, y_pos))
        entry.bind('<FocusOut>', lambda e: self._float_label_down(entry, label, label_text, y_pos))
        
        if is_password:
            self.password_entry = entry
        else:
            self.username_entry = entry

    def _float_label_up(self, label, y_pos):
        """标签上浮动画"""
        self.login_card.itemconfigure(label, fill='#3498db', font=('微软雅黑', 9))
        for i in range(5):
            self.login_card.move(label, 0, -1)
            self.window.update()
            time.sleep(0.02)

    def _float_label_down(self, entry, label, default_text, y_pos):
        """标签下沉动画"""
        if not entry.get():
            self.login_card.itemconfigure(label, fill='#95a5a6', font=('微软雅黑', 11))
            for i in range(5):
                self.login_card.move(label, 0, 1)
                self.window.update()
                time.sleep(0.02)

    def _attempt_login(self):
        """登录验证"""
        username = self.username_entry.get()
        password = self.password_entry.get()
        self.app.show_main()
        """
        if username == "admin" and password == "123456":
            self.app.show_main()
        else:
            messagebox.showerror("错误", "用户名或密码错误")
        """
    
    def _bind_events(self):
        """事件绑定"""
        self.window.bind("<Return>", lambda e: self._attempt_login())
        self.window.bind("<Escape>", lambda e: self.window.attributes("-fullscreen", False))

    def close(self):
        self.window.destroy()

class MainWindow:
    def __init__(self, root, app):
        self.app = app
        self.window = tk.Toplevel(root)
        self.window.protocol("WM_DELETE_WINDOW", self.safe_close)
        self.music_lock = threading.Lock()
        self.running = True
        self.weather_retry_count = 0
        self._first_weather_update = True
        self.music_initialized = False
        self.light_status = False  # 灯光状态
        self.brightness = 50 
        
        self._setup_ui()
        self._init_pages()
        self._init_network()
        self._init_pygame()
        self._start_threads()
        self.show_main()
        self.debug_mode = True  # 设为True显示详细错误信息
    
    def _setup_ui(self):
        self.window.attributes('-fullscreen', True)
        self.window.configure(bg='#2c3e50')
        
        # 主容器
        main_frame = tk.Frame(self.window, bg='#2c3e50')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 顶部标题栏
        title_frame = tk.Frame(main_frame, bg='#34495e', height=60)
        title_frame.pack(fill=tk.X)

        # 系统标题
        tk.Label(title_frame, text="智能家居系统", 
                font=('微软雅黑', 24, 'bold'), 
                bg='#34495e', fg='white').pack(side=tk.LEFT, padx=20)

        # 天气信息
        self.weather_label = tk.Label(title_frame, 
                                    text="成都 --℃ --",
                                    font=('微软雅黑', 14), 
                                    bg='#34495e',
                                    fg='white')
        self.weather_label.pack(side=tk.RIGHT, padx=20)

        # 内容区域
        self.content_area = tk.Frame(main_frame, bg='#2c3e50')
        self.content_area.pack(fill=tk.BOTH, expand=True)

        # 底部导航栏
        nav_frame = tk.Frame(main_frame, bg='#34495e', height=80)
        nav_frame.pack(fill=tk.X, side=tk.BOTTOM)

        # 功能按钮
        buttons = [
            ("环境监测", self.show_environment),
            ("视频监控", self.show_video),
            ("音乐播放", self.show_music),
            ("邮箱通知", self.show_emails),
            ("灯光控制", self.show_light) 
        ]

        btn_container = tk.Frame(nav_frame, bg='#34495e')
        btn_container.place(relx=0.5, rely=0.5, anchor='center')

        for text, cmd in buttons:
            btn = tk.Button(btn_container, text=text, command=cmd,
                          font=('微软雅黑',14), 
                          bg='#3498db', fg='white',
                          width=15, height=2,
                          relief='flat')
            btn.pack(side=tk.LEFT, padx=15)

    def _init_pygame(self):
        """安全初始化音频系统"""
        try:
            pygame.init()  # !!! 添加主模块初始化
            pygame.mixer.init(
                frequency=44100,
                size=-16,
                channels=2,
                buffer=4096,
                allowedchanges=pygame.AUDIO_ALLOW_ANY_CHANGE  # !!! 添加兼容性参数
            )
            self.music_initialized = True
            print("[DEBUG] 音频系统初始化成功")  # !!! 添加调试信息
        except Exception as e:
            print(f"音频初始化失败: {str(e)}")
            self.music_initialized = False
            messagebox.showerror("音频错误", f"初始化失败: {str(e)}")  # !!! 添加错误提示

    def _init_network(self):
        """增强网络连接处理"""
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(5)
        try:
            self.sock.connect((self.app.server_ip, 12581))
            print("设备连接成功")
        except socket.timeout:
            messagebox.showwarning("超时", "设备连接超时，请检查IP和网络")
        except Exception as e:
            messagebox.showerror("连接错误", f"设备连接失败: {str(e)}")
        finally:
            if not hasattr(self.sock, 'send'):
                self.sock.close()

        self.sock_v = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock_v.settimeout(5)
        try:
            self.sock_v.connect((self.app.server_ip, 12580))
            print("设备连接成功")
        except socket.timeout:
            messagebox.showwarning("超时", "设备连接超时，请检查IP和网络")
        except Exception as e:
            messagebox.showerror("连接错误", f"设备连接失败: {str(e)}")
        finally:
            if not hasattr(self.sock_v, 'send'):
                self.sock_v.close()

    def _init_pages(self):
        """初始化所有页面"""
        self.main_page = self._create_main_page()
        self.env_page = self._create_env_page()
        self.video_page = self._create_video_page()
        self.music_page = self._create_music_page()
        self.email_page = self._create_email_page()
        self.light_page = self._create_light_page()
        for page in [self.env_page, self.video_page, 
                    self.music_page, self.email_page,
                    self.light_page]:
            page.pack_forget()

    def _create_main_page(self):
        """主页面"""
        frame = tk.Frame(self.content_area, bg='#2c3e50')
        time_container = tk.Frame(frame, bg='#2c3e50')
        time_container.place(relx=0.5, rely=0.5, anchor='center')

        # 日期显示
        self.date_label = tk.Label(time_container, 
                                 text="----年--月--日 星期-",
                                 font=('微软雅黑', 20),
                                 bg='#2c3e50',
                                 fg='#95a5a6')
        self.date_label.pack(pady=10)

        # 时钟显示
        self.clock_label = tk.Label(time_container, 
                                  text="--:--:--",
                                  font=('Courier New', 48, 'bold'),
                                  bg='#2c3e50',
                                  fg='#3498db')
        self.clock_label.pack()
        return frame

    def _start_threads(self):
        """优化线程启动顺序"""
        # 添加音乐事件处理线程 !!!
        threading.Thread(target=self._check_music_events, daemon=True).start()
        # 优先启动时间线程
        threading.Thread(target=self._update_datetime, daemon=True).start()
        # 立即启动天气线程
        threading.Thread(target=self._update_weather, daemon=True).start()
        # 其他功能线程
        threading.Thread(target=self._update_video, daemon=True).start()
        threading.Thread(target=self._update_sensors, daemon=True).start()
        threading.Thread(target=self._check_emails, daemon=True).start()

    def _update_datetime(self):
        """增强时间更新线程"""
        weekdays = ["一", "二", "三", "四", "五", "六", "日"]
        last_second = -1
        
        # 初始更新
        self._update_clock_ui("--:--:--", "正在初始化...")
        
        while self.running:
            now = datetime.now()
            current_second = now.second
            if current_second != last_second:
                try:
                    time_str = now.strftime("%H:%M:%S")
                    date_str = f"{now.year}年{now.month:02d}月{now.day:02d}日 星期{weekdays[now.weekday()]}"
                    self._update_clock_ui(time_str, date_str)
                    last_second = current_second
                except Exception as e:
                    print(f"时间更新异常: {str(e)}")
                finally:
                    # 精确控制更新时间
                    sleep_time = 1.0 - (datetime.now().microsecond / 1_000_000)
                    time.sleep(max(0.1, sleep_time))

    def _update_weather(self):
        """增强版天气更新方法"""
        url = f"http://api.openweathermap.org/data/2.5/weather?" \
              f"lat={CHENGDU_LOCATION['lat']}&lon={CHENGDU_LOCATION['lng']}" \
              f"&appid={OPENWEATHER_API_KEY}&units=metric&lang=zh_cn"
        
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                        'AppleWebKit/537.36 (KHTML, like Gecko) '
                        'Chrome/91.0.4472.124 Safari/537.36'
        }

        while self.running:
            try:
                # 网络状态检测
                if not self._check_internet():
                    self._update_weather_ui("网络", "不可用")
                    if self.debug_mode:
                        print("[DEBUG] 网络连接不可用")
                    time.sleep(10)
                    continue

                # API请求
                start_time = time.time()
                response = requests.get(url, headers=headers, timeout=10)
                if self.debug_mode:
                    print(f"[DEBUG] 天气API响应时间：{time.time()-start_time:.2f}秒")
                    print(f"[DEBUG] HTTP状态码：{response.status_code}")

                if response.status_code == 200:
                    data = response.json()
                    if self.debug_mode:
                        print(f"[DEBUG] 完整API响应：{data}")
                    
                    # 解析温度
                    temp = data.get('main', {}).get('temp')
                    if temp is None:
                        raise ValueError("温度数据缺失")
                    
                    # 解析天气描述
                    weather_desc = data.get('weather', [{}])[0].get('description', '未知')
                    if not weather_desc:
                        weather_desc = "未知"

                    # 更新UI
                    self._update_weather_ui(f"{float(temp):.1f}", weather_desc.capitalize())
                    self.weather_retry_count = 0
                    time.sleep(1800)  # 30分钟更新
                else:
                    error_msg = f"API错误[{response.status_code}]"
                    if response.status_code == 401:
                        error_msg += " (无效API密钥)"
                    self._handle_weather_error(error_msg)

            except requests.exceptions.Timeout:
                self._handle_weather_error("请求超时")
            except requests.exceptions.RequestException as e:
                self._handle_weather_error(f"网络错误: {str(e)}")
            except ValueError as e:
                self._handle_weather_error(f"数据错误: {str(e)}")
            except Exception as e:
                self._handle_weather_error(f"未知错误: {str(e)}")

    def _check_internet(self, timeout=3):
        """增强网络检测"""
        test_urls = [
            'https://www.baidu.com',
            'https://www.qq.com',
            'https://www.163.com'
        ]
        for url in test_urls:
            try:
                response = requests.get(url, timeout=timeout)
                if response.status_code == 200:
                    return True
            except:
                continue
        return False

    def _update_clock_ui(self, time_str, date_str):
         """线程安全更新时间显示"""
         self.window.after(0, lambda: [
            self.clock_label.config(text=time_str),
            self.date_label.config(text=date_str)
        ])

    def _update_weather_ui(self, temp, desc):
        """线程安全更新天气显示"""
        self.window.after(0, lambda: self.weather_label.config(
            text=f"成都 {temp}℃ {desc}"
        ))

    def _handle_weather_error(self, error_msg):
        """增强错误处理"""
        self.weather_retry_count += 1
        retry_delay = min(60 * self.weather_retry_count, 300)  # 最大间隔5分钟
        
        if self.debug_mode:
            print(f"[DEBUG] 天气更新失败: {error_msg}")
            print(f"[DEBUG] 将在{retry_delay}秒后重试")

        # 显示错误信息（前3次显示具体错误，后续显示通用提示）
        display_msg = error_msg if self.weather_retry_count <= 3 else "服务暂时不可用"
        self._update_weather_ui("--", display_msg)
        
        time.sleep(retry_delay)
# ===== 灯光控制页面实现 =====
    def _create_light_page(self):
        """灯光控制页面"""
        frame = tk.Frame(self.content_area, bg='#2c3e50')
        
        # 页面头部
        header = tk.Frame(frame, bg='#34495e')
        header.pack(fill=tk.X, pady=10)
        
        back_btn = tk.Button(header, 
                            text="← 返回", 
                            command=self.show_main,
                            font=('微软雅黑',12), 
                            bg='#e67e22', 
                            fg='white')
        back_btn.pack(side=tk.LEFT, padx=20)
        
        title_label = tk.Label(header, 
                             text="灯光控制", 
                             font=('微软雅黑',16,'bold'), 
                             bg='#34495e', 
                             fg='white')
        title_label.pack(side=tk.LEFT, padx=20)
        
        # 控制面板
        control_frame = tk.Frame(frame, bg='#2c3e50')
        control_frame.pack(pady=50, expand=True)

        # 开关按钮
        self.light_switch = tk.Button(control_frame, 
                                    text="关", 
                                    command=self.toggle_light,
                                    font=('微软雅黑', 24),
                                    width=8,
                                    bg='#e74c3c',
                                    activebackground='#c0392b',
                                    relief='flat')
        self.light_switch.pack(pady=20)

        # 亮度调节
        brightness_frame = tk.Frame(control_frame, bg='#2c3e50')
        brightness_frame.pack(pady=20)
        
        tk.Label(brightness_frame, 
               text="亮度调节", 
               font=('微软雅黑',14), 
               bg='#2c3e50', 
               fg='white').pack(side=tk.LEFT)
        
        self.brightness_scale = tk.Scale(brightness_frame, 
                                       from_=0, 
                                       to=100, 
                                       orient=tk.HORIZONTAL,
                                       command=self.update_brightness,
                                       length=200,
                                       bg='#2c3e50',
                                       fg='white',
                                       troughcolor='#34495e',
                                       sliderrelief='flat',
                                       highlightthickness=0)
        self.brightness_scale.set(self.brightness)
        self.brightness_scale.pack(side=tk.LEFT, padx=20)

        return frame


# ===== 灯光控制逻辑 =====
    def toggle_light(self):
        """切换灯光开关状态"""
        self.light_status = not self.light_status
        
        if self.light_status:
            # 开灯逻辑
            self.light_switch.config(text="开", bg='#2ecc71')
            command = f"LIGHT_ON {self.brightness}"
        else:
            # 关灯逻辑
            self.light_switch.config(text="关", bg='#e74c3c')
            command = "LIGHT_OFF"
        
        self._send_command(command)

    def update_brightness(self, value):
        """更新亮度值"""
        self.brightness = int(value)
        if self.light_status:  # 仅在开灯状态发送亮度调整
            self._send_command(f"BRIGHTNESS {self.brightness}")

    def _send_command(self, command):
        """发送控制命令到服务器"""
        try:
            if hasattr(self, 'sock') and self.sock:
                # 添加时间戳和校验位
                full_cmd = f"#{command}|{time.time()}#\n"
                self.sock.sendall(full_cmd.encode('utf-8'))
                
                # 调试输出
                if self.debug_mode:
                    print(f"[DEBUG] 已发送命令: {full_cmd.strip()}")
        except Exception as e:
            error_msg = f"控制命令发送失败: {str(e)}"
            messagebox.showerror("通信错误", error_msg)
            if self.debug_mode:
                print(f"[ERROR] {error_msg}")
                
    def _create_env_page(self):
        """环境监测页面"""
        frame = tk.Frame(self.content_area, bg='#2c3e50')
        
        # 页面头部
        header = tk.Frame(frame, bg='#34495e')
        header.pack(fill=tk.X, pady=10)
        
        tk.Button(header, text="← 返回", command=self.show_main,
                 font=('微软雅黑',12), bg='#e67e22', fg='white').pack(side=tk.LEFT, padx=20)
        tk.Label(header, text="环境监测", font=('微软雅黑',16,'bold'), 
                bg='#34495e', fg='white').pack(side=tk.LEFT, padx=20)
        
        # 内容区域
        content = tk.Frame(frame, bg='#2c3e50')
        content.pack(pady=50, expand=True)
        
        # 温度显示
        temp_frame = tk.Frame(content, bg='#2c3e50')
        tk.Label(temp_frame, text="🌡️", font=('Arial',24), bg='#2c3e50', fg='#e74c3c').pack(side=tk.LEFT)
        self.temp_label = tk.Label(temp_frame, text="--℃", font=('微软雅黑',18), bg='#2c3e50', fg='white')
        self.temp_label.pack(side=tk.LEFT, padx=10)
        temp_frame.pack(side=tk.LEFT)
        
        # 湿度显示
        humi_frame = tk.Frame(content, bg='#2c3e50')
        tk.Label(humi_frame, text="💧", font=('Arial',24), bg='#2c3e50', fg='#3498db').pack(side=tk.LEFT)
        self.humi_label = tk.Label(humi_frame, text="--%", font=('微软雅黑',18), bg='#2c3e50', fg='white')
        self.humi_label.pack(side=tk.LEFT, padx=10)
        humi_frame.pack(side=tk.LEFT)
        
        # 报警状态
        self.alert_label = tk.Label(content, text="状态正常", font=('微软雅黑',14), 
                                  bg='#2ecc71', fg='white', padx=15, pady=5)
        self.alert_label.pack(side=tk.RIGHT, padx=20)
        
        return frame

    def _create_video_page(self):
        """视频监控页面"""
        frame = tk.Frame(self.content_area, bg='black')
        
        # 页面头部
        header = tk.Frame(frame, bg='#34495e')
        header.pack(fill=tk.X, pady=10)
        
        tk.Button(header, text="← 返回", command=self.show_main,
                 font=('微软雅黑',12), bg='#e67e22', fg='white').pack(side=tk.LEFT, padx=20)
        tk.Label(header, text="视频监控", font=('微软雅黑',16,'bold'), 
                bg='#34495e', fg='white').pack(side=tk.LEFT, padx=20)
        
        self.video_label = tk.Label(frame)
        self.video_label.pack(expand=True, fill=tk.BOTH)
        
        return frame

    def _create_music_page(self):
        """音乐播放页面"""
        frame = tk.Frame(self.content_area, bg='#2c3e50')
        
        # 页面头部
        header = tk.Frame(frame, bg='#34495e')
        header.pack(fill=tk.X, pady=10)
        
        tk.Button(header, text="← 返回", command=self.show_main,
                 font=('微软雅黑',12), bg='#e67e22', fg='white').pack(side=tk.LEFT, padx=20)
        tk.Label(header, text="音乐播放", font=('微软雅黑',16,'bold'), 
                bg='#34495e', fg='white').pack(side=tk.LEFT, padx=20)
        
        # 控制面板
        control_frame = tk.Frame(frame, bg='#2c3e50')
        control_frame.pack(pady=20)
        
        self.play_btn = tk.Button(control_frame, text="▶", font=('Arial',24), 
                                 command=self.toggle_music, width=4)
        self.play_btn.pack(side=tk.LEFT, padx=10)
        
        self.volume_scale = tk.Scale(control_frame, from_=0, to=100, orient=tk.HORIZONTAL,
                                    bg='#2c3e50', fg='white', troughcolor='#34495e', length=150)
        self.volume_scale.set(50)
        self.volume_scale.pack(side=tk.LEFT, padx=10)
        
        # 播放列表
        list_frame = tk.Frame(frame, bg='#2c3e50')
        list_frame.pack(fill=tk.BOTH, expand=True, padx=20)
        
        self.music_list = tk.Listbox(list_frame, bg='#34495e', fg='white', 
                                    selectbackground='#3498db', font=('微软雅黑',12))
        scrollbar = tk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.music_list.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.music_list.yview)
        self.music_list.pack(fill=tk.BOTH, expand=True)
    
        # 添加文件选择按钮
        self.add_btn = tk.Button(control_frame, text="📁 添加文件", 
                           command=self._add_music_file,
                           font=('微软雅黑',12), width=10)
        self.add_btn.pack(side=tk.LEFT, padx=5)
    
        # 上一曲/下一曲控制
        self.prev_btn = tk.Button(control_frame, text="⏮", 
                            command=self.play_previous,
                            font=('Arial',14), width=3)
        self.prev_btn.pack(side=tk.LEFT, padx=5)
    
        self.next_btn = tk.Button(control_frame, text="⏭", 
                            command=self.play_next,
                            font=('Arial',14), width=3)
        self.next_btn.pack(side=tk.LEFT, padx=5)
    
        # 进度条
        self.progress = ttk.Scale(control_frame, from_=0, to=100, 
                            orient=tk.HORIZONTAL, length=200)
        self.progress.pack(side=tk.LEFT, padx=10)
    
        # 初始化音乐播放器
        self.current_track = None
        self.playlist = []
        self.current_index = -1
        pygame.mixer.music.set_endevent(pygame.USEREVENT)  # 注册播放完成事件
        self.playlist = []
        self.current_index = -1
        self.music_list.email_data = []  # 用于存储完整路径
        
        # 添加音量控制绑定 !!!
        self.volume_scale.bind("<ButtonRelease-1>", self._update_volume)
        return frame
    def _update_volume(self, event=None):  # !!! 新增音量控制方法
        """更新音量"""
        if self.music_initialized:
            volume = self.volume_scale.get() / 100
            pygame.mixer.music.set_volume(volume)

    def _check_music_events(self):
        """增强的事件监听循环"""
        while self.running:
            try:
                # 更高效的事件检查
                for event in pygame.event.get(pygame.USEREVENT):
                    if event.type == pygame.USEREVENT:
                        print("[DEBUG] 检测到播放完成事件")  # 调试输出
                        self.play_next()
            
                # 降低CPU占用
                time.sleep(0.2)
            
            except pygame.error as e:
                print(f"[ERROR] 事件监听失败: {str(e)}")
                break
    def _add_music_file(self):
        """添加音乐文件"""
        files = filedialog.askopenfilenames(
            filetypes=[("音频文件", "*.mp3 *.wav *.ogg")]
        )
        for f in files:
            if f not in self.playlist:
                self.playlist.append(f)
                self.music_list.insert(tk.END, os.path.basename(f))

    def toggle_music(self):
        """增强播放控制"""
        if not self.current_track:
            self.play_next()
        elif pygame.mixer.music.get_busy():
            pygame.mixer.music.pause()
            self.play_btn.config(text="▶")
        else:
            pygame.mixer.music.unpause()
            self.play_btn.config(text="⏸")

    def play_next(self):
        """线程安全的下一曲播放"""
        with self.music_lock:
            if self.playlist:
             # 循环索引计算
                new_index = (self.current_index + 1) % len(self.playlist)
                self.current_index = new_index
                self._load_track_safe()
    def play_previous(self):
        """线程安全的上一曲播放"""
        with self.music_lock:
            if self.playlist:
                new_index = (self.current_index - 1) % len(self.playlist)
                self.current_index = new_index
                self._load_track_safe()
    def _load_track_safe(self):
        """带资源清理的曲目加载"""
        try:
            # 停止当前播放并释放资源
            if pygame.mixer.music.get_busy():
                pygame.mixer.music.stop()
                time.sleep(0.1)  # 等待资源释放
        
            # 获取实际路径
            filepath = self.playlist[self.current_index]
            print(f"[DEBUG] 正在加载: {filepath}")  # 调试用
        
            # 处理特殊字符路径
            encoded_path = filepath.encode('utf-8', 'surrogateescape').decode('utf-8')
        
            # 加载并播放
            pygame.mixer.music.load(encoded_path)
            pygame.mixer.music.play()
        
            # 更新UI状态
            self.window.after(0, lambda: [
                self.play_btn.config(text="⏸"),
                self.now_playing.config(text=f"正在播放: {os.path.basename(filepath)}")
            ])
        
            # 启动进度更新
            self._update_progress()
        
        except pygame.error as e:
            messagebox.showerror("播放错误", f"无法加载音频文件:\n{str(e)}")
        except Exception as e:
            messagebox.showerror("意外错误", f"发生未知错误:\n{str(e)}")
    def _load_track(self, filepath):
        """加载音乐文件"""
        try:
            # 处理中文路径问题 !!!
            encoded_path = filepath.encode('utf-8', 'surrogateescape').decode('utf-8')
            pygame.mixer.music.load(encoded_path)
            pygame.mixer.music.play()
            self.current_track = filepath
            self.play_btn.config(text="⏸")
            self._update_progress()
            
            # 显示当前播放 !!!
            self.now_playing.config(text=f"正在播放: {os.path.basename(filepath)}")
        except Exception as e:
            messagebox.showerror("错误", f"无法播放文件:\n{str(e)}")

    def _update_progress(self):
        """更健壮的进度更新"""
        if pygame.mixer.music.get_busy() and self.current_index != -1:
            try:
                # 获取精确时长
                filepath = self.playlist[self.current_index]
                sound = pygame.mixer.Sound(filepath)
                total = sound.get_length()
                pos = pygame.mixer.music.get_pos() / 1000
            
                # 更新进度条
                self.progress.config(to=total)
                self.progress.set(pos)
            
                # 递归调用保持更新
                self.window.after(500, self._update_progress)
            
            except Exception as e:
                print(f"[WARNING] 进度更新失败: {str(e)}")
    def _create_email_page(self):
        """邮箱页面"""
        frame = tk.Frame(self.content_area, bg='#2c3e50')
    
        # 页面头部
        header = tk.Frame(frame, bg='#34495e')
        header.pack(fill=tk.X, pady=10)
    
        tk.Button(header, text="← 返回", command=self.show_main,
             font=('微软雅黑',12), bg='#e67e22', fg='white').pack(side=tk.LEFT, padx=20)
        tk.Label(header, text="邮件通知", font=('微软雅黑',16,'bold'), 
            bg='#34495e', fg='white').pack(side=tk.LEFT, padx=20)
    
        # 新增刷新按钮在右侧 !!!
        refresh_btn = tk.Button(header, text="刷新邮件", command=self._load_emails,
                           font=('微软雅黑',12), bg='#3498db', fg='white')
        refresh_btn.pack(side=tk.RIGHT, padx=20)
    
        # 邮件列表容器
        list_container = tk.Frame(frame, bg='#2c3e50')
        list_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
    
        # 邮件列表
        self.email_list = tk.Listbox(list_container, bg='#34495e', fg='white',
                                selectbackground='#3498db', font=('微软雅黑',12))
        scrollbar = tk.Scrollbar(list_container)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    
        self.email_list.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.email_list.yview)
        self.email_list.pack(fill=tk.BOTH, expand=True)
    
        # 邮件详情显示
        self.email_text = tk.Text(list_container, wrap=tk.WORD, 
                            bg='#34495e', fg='white',
                            font=('微软雅黑',12))
        self.email_text.pack(fill=tk.BOTH, expand=True)
    
        # 初始化邮件数据存储
        self.email_list.email_data = []
    
        # 绑定列表点击事件
        self.email_list.bind('<<ListboxSelect>>', self._show_email_detail)
    
        return frame
    def _load_emails(self):
        """增强邮件加载方法"""
        self.email_list.delete(0, tk.END)
        self.email_list.email_data.clear()
    
        mail = None
        try:
            mail = imaplib.IMAP4_SSL(EMAIL_CONFIG["server"], 993, timeout=15)
            mail.login(EMAIL_CONFIG["account"], EMAIL_CONFIG["password"])
        
            status, count = mail.select('inbox')
            if status != 'OK':
                raise Exception("无法选择收件箱")
        
            status, data = mail.search(None, 'ALL')
            if status != 'OK':
                raise Exception("邮件搜索失败")
        
            email_ids = data[0].split()[-5:]
            print(f"[DEBUG] 找到邮件数量: {len(email_ids)}")
        
            for num in reversed(email_ids):
                typ, msg_data = mail.fetch(num, '(RFC822)')
                if typ != 'OK':
                    continue
            
                msg = email.message_from_bytes(msg_data[0][1])
                subject = self._decode_header(msg.get('Subject', '无主题'))
                from_ = self._decode_header(msg.get('From', '未知发件人'))
                date = self._parse_date(msg.get('Date', ''))
            
                display_str = f"{date} | {from_:<20} | {subject[:30]}"
                self.email_list.insert(tk.END, display_str)
                self.email_list.email_data.append(msg)
            
            mail.close()
        
        except Exception as e:
            error_msg = f"邮件加载失败: {str(e)}"
            print(f"[ERROR] {error_msg}")
            messagebox.showerror("错误", error_msg)
        finally:
            if mail:
                try:
                    mail.logout()
                except:
                    pass
    def _show_email_detail(self, event):
        """显示邮件详情"""
        self.email_text.delete(1.0, tk.END)
        index = self.email_list.curselection()
        if index:
            msg = self.email_list.email_data[index[0]]
        
            # 解析邮件内容
            body = ""
            for part in msg.walk():
                content_type = part.get_content_type()
                if content_type == "text/plain":
                    body = part.get_payload(decode=True).decode(
                        part.get_content_charset() or 'utf-8', 'ignore'
                    )
                    break
        
            # 构建显示内容
            detail = f"发件人: {self._decode_header(msg['From'])}\n"
            detail += f"日期: {self._parse_date(msg['Date'])}\n"
            detail += f"主题: {self._decode_header(msg['Subject'])}\n\n"
            detail += "="*50 + "\n"
            detail += body[:2000]  # 显示前2000字符防止卡顿
            self.email_text.insert(tk.END, detail)

        # 新增辅助方法
    
    def _decode_header(self, header):
        """解码邮件头"""
        decoded = decode_header(header)
        return ''.join([t[0].decode(t[1] or 'utf-8') if isinstance(t[0], bytes) else t[0] 
                       for t in decoded])

    def _parse_date(self, date_str):
        """解析邮件日期"""
        try:
            return datetime.strptime(
                date_str[:24], '%a, %d %b %Y %H:%M:%S'
            ).strftime('%Y-%m-%d %H:%M')
        except:
            return date_str[:16]

     # 以下功能方法保持不变
    def toggle_music(self):
        if pygame.mixer.music.get_busy():
            pygame.mixer.music.pause()
            self.play_btn.config(text="▶")
        else:
            pygame.mixer.music.unpause()
            self.play_btn.config(text="⏸")

    def _update_video(self):
        buffer_size = 640 * 480 * 3 + 1  # RGB每像素3字节
        
        while self.running:
            try:
                # 接收完整的帧数据 
                frame_data = b''
                while len(frame_data) < buffer_size:
                    chunk = self.sock_v.recv(buffer_size - len(frame_data))
                    if not chunk:
                        raise ConnectionError("视频连接断开")
                    frame_data += chunk
                

                
                if frame_data[0] != 49:
                    print("丢弃此帧", frame_data[0], frame_data[1], len(frame_data))
                    frame_data = b''
                    continue
                    # 丢弃0xFF字节，跳过
                #print("ok", frame_data[0], frame_data[1], len(frame_data))
                frame_data = frame_data[1:]
                
                # 将接收到的数据转换为numpy数组
                frame = np.frombuffer(frame_data, dtype=np.uint8)
                frame = frame.reshape((480, 640, 3))  # 重塑为图像形状
                
                # 转换为PhotoImage并显示
                img = Image.fromarray(frame)
                
                # 获取video_label的尺寸
                label_width = self.video_label.winfo_width()
                label_height = self.video_label.winfo_height()
                #print("*********", label_width, label_width)
                # 调整图像大小以适应label的大小
                img = img.resize((label_width, label_height), Image.Resampling.LANCZOS)
                
                photo = ImageTk.PhotoImage(image=img)
                
                # 更新显示（使用after确保在主线程中更新UI）
                self.window.after(0, lambda: self.video_label.configure(image=photo))
                self.video_label.image = photo  # 保持引用防止垃圾回收
                
            except ConnectionError as e:
                print(f"视频连接错误: {str(e)}")
                break
            except Exception as e:
                print(f"视频处理错误: {str(e)}")
                time.sleep(1)
        """
        cap = cv2.VideoCapture(f'http://{self.app.server_ip}:8080/?action=stream')
        while self.running:
            ret, frame = cap.read()
            if ret:
                img = cv2.cvtColor(cv2.resize(frame, (800,600)), cv2.COLOR_BGR2RGB)
                self.video_label.img = ImageTk.PhotoImage(Image.fromarray(img))
                self.video_label.configure(image=self.video_label.img)
        """
    def _update_sensors(self):
        while self.running:
            try:
                data = self.sock.recv(1024).decode()
                if "TEMP" in data:
                    print(data)
                    temp = data.split(',')[0].split(':')[1]
                    humi = data.split(',')[1].split(':')[1]
                    self.temp_label.config(text=f"{temp}℃")
                    self.humi_label.config(text=f"{humi}%")
                    self.alert_label.config(
                        text="🔥 火灾警报！" if float(temp) > 50 else "状态正常",
                        bg='#e74c3c' if float(temp) > 50 else '#2ecc71'
                    )
            except: pass

    def _check_emails(self):
        while self.running:
            mail = None
            try:
                mail = imaplib.IMAP4_SSL(EMAIL_CONFIG["server"], 993)
                mail.login(EMAIL_CONFIG["account"], EMAIL_CONFIG["password"])
                mail.select('inbox')
                status, messages = mail.search(None, 'UNSEEN')
                if status == 'OK':
                    count = len(messages[0].split())
                    self.email_list.insert(tk.END, f"未读邮件数: {count}")
            except Exception as e:
                print(f"邮件错误: {str(e)}")
            finally:
                if mail:
                    try:
                        mail.logout()
                    except:
                        pass
            time.sleep(60)

    def _load_emails(self):
        self.email_list.delete(0, tk.END)
        try:
            with imaplib.IMAP4_SSL(EMAIL_CONFIG["server"], 993) as mail:
                mail.login(EMAIL_CONFIG["account"], EMAIL_CONFIG["password"])
                mail.select('inbox')
                _, data = mail.search(None, 'UNSEEN')
                for num in data[0].split()[-5:]:
                    _, msg_data = mail.fetch(num, '(RFC822)')
                    msg = email.message_from_bytes(msg_data[0][1])
                    subject = decode_header(msg.get('Subject', '无主题'))[0][0]
                    if isinstance(subject, bytes):
                        subject = subject.decode('utf-8')
                    self.email_list.insert(tk.END, f"主题: {subject[:50]}")
        except Exception as e:
            messagebox.showerror("错误", f"邮件加载失败: {str(e)}")
    def _switch_page(self, new_page):
        for page in [self.main_page, self.env_page, 
                    self.video_page, self.music_page, self.email_page]:
            if page.winfo_ismapped():
                page.pack_forget()
        new_page.pack(fill=tk.BOTH, expand=True)

    def show_environment(self): self._switch_page(self.env_page)
    def show_video(self): self._switch_page(self.video_page)
    def show_music(self): self._switch_page(self.music_page)
    def show_emails(self): self._switch_page(self.email_page)
    def show_main(self): self._switch_page(self.main_page)
    def show_light(self):self._switch_page(self.light_page)
    
    def safe_close(self):
        self.running = False
        try:
            if hasattr(self, 'sock'):
                self.sock.close()
        except Exception as e:
            print(f"Socket关闭异常: {str(e)}")
        pygame.mixer.quit()
        self.window.destroy()
        self.app.root.destroy()



if __name__ == "__main__":
    AppManager()
    